﻿
namespace Patterns4Net.Core.CecilExtensions
{
    using System;
    using System.Diagnostics.Contracts;
    using Mono.Cecil;

    public static class ResolveExtension
    {
        public static TypeDefinition SafeResolve(this TypeReference reference, string exceptionMessage = "")
        {
            Contract.Requires(reference != null);
            Contract.Ensures(Contract.Result<TypeDefinition>() != null);

            TypeDefinition resolvedType;
            try
            {
                resolvedType = reference.Resolve();
            }
            catch (Exception exception)
            {
                // There is no documentation on which exceptions Resolve may throw
                throw new ResolveException(reference, exceptionMessage, exception);
            }

            if (resolvedType == null)
            {
                throw new ResolveException(reference, exceptionMessage);
            }

            return resolvedType;
        }

        public class ResolveExtensionsException : ResolveException
        {
            private const string BaseMessage = @"At some point program called Mono.Cecil's method Resolve on type {0} with name {1}. ";

            private const string MessageException = @"This undocumented method has thrown an exception of type {0}. Additional info: {1}.";

            private const string MessageNull = @"This undocumented method has returned null reference which is not expected. Additional info: {0}.";

            public ResolveExtensionsException(MemberReference member, string info)
                : base(member, FormatBaseMessage(member) + string.Format(MessageNull, info), null)
            {
                Contract.Requires(member != null);
            }

            public ResolveExtensionsException(MemberReference member, string info, Exception inner)
                : base(member, FormatBaseMessage(member) + string.Format(MessageException, inner.GetType().Name, info), inner)
            {
                Contract.Requires(member != null);
                Contract.Requires(inner != null);
            }

            private static string FormatBaseMessage(MemberReference member)
            {
                Contract.Requires(member != null);
                return string.Format(BaseMessage, member.GetType().Name, member.FullName);
            }
        }
    }
}
